/*
* libjingle
* Copyright 2004--2005, Google Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
*  1. Redistributions of source code must retain the above copyright notice,
*     this list of conditions and the following disclaimer.
*  2. Redistributions in binary form must reproduce the above copyright notice,
*     this list of conditions and the following disclaimer in the documentation
*     and/or other materials provided with the distribution.
*  3. The name of the author may not be used to endorse or promote products
*     derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#include "talk/examples/crossfirewall/cfsessionclient.h"
//#include "talk/examples/crossfirewall/strconstants.h"

#include "talk/base/common.h"
#include "talk/base/logging.h"
#include "talk/base/stringutils.h"
#include "talk/p2p/base/parsing.h"
#include "talk/xmpp/constants.h"
#include "talk/xmllite/qname.h"

using namespace talk_base;

namespace cricket {

	CFSessionClient::CFSessionClient(buzz::XmppClient* xmppclient, bool bhttpallocator)
	{
		worker_thread_ = new Thread();
		worker_thread_->Start();

		network_manager_ = new talk_base::BasicNetworkManager();
		if(bhttpallocator) {
			port_allocator_ = new cricket::HttpPortAllocator(network_manager_, "str");
		}
		else {
			// TODO: Decide if the relay address should be specified here.
			talk_base::SocketAddress stun_addr("stun.l.google.com", 19302);   talk_base::SocketAddress relay_u_addr("relay.google.com", 19295);
			talk_base::SocketAddress relay_t_addr("relay.google.com", 19294); talk_base::SocketAddress relay_ssl_addr("relay.google.com", 443);
			port_allocator_ = new cricket::BasicPortAllocator(network_manager_, stun_addr, relay_u_addr, relay_t_addr, relay_ssl_addr);
		}

		session_manager_ = new cricket::SessionManager(port_allocator_, worker_thread_);
		session_manager_->SignalRequestSignaling.connect(this, &CFSessionClient::OnRequestSignaling);
		session_manager_->OnSignalingReady();

		session_manager_task_ = new SessionManagerTask(xmppclient, session_manager_);
		session_manager_task_->EnableOutgoingMessages();
		session_manager_task_->Start();

		// add ourselves to the client map.
		session_manager_->AddClient(NS_JINGLE_RTP, this);
	}

	CFSessionClient::~CFSessionClient()
	{
		// Remove ourselves from the client map.
		session_manager_->RemoveClient(NS_JINGLE_RTP);

		if(session_manager_) { delete session_manager_; session_manager_  = NULL; }
		if(port_allocator_)  { delete port_allocator_;  port_allocator_   = NULL; }
		if(network_manager_) { delete network_manager_; network_manager_  = NULL; }
		if(worker_thread_)   { worker_thread_->Stop();  delete worker_thread_;   worker_thread_  = NULL; }
	}

	Session *CFSessionClient::CreateSession(std::string name, std::string session_type)
	{
		Session *session = session_manager_->CreateSession(name, session_type);
		return session;
	}

	void CFSessionClient::DestroySession(Session *session)
	{
		session_manager_->DestroySession(session);
	}

	void CFSessionClient::OnSessionCreate(Session *session, bool received_initiate) 
	{
		if(received_initiate)
			session->SignalState.connect(this, &CFSessionClient::OnSessionState);
	}

	void CFSessionClient::OnSessionDestroy(Session *session) 
	{
		SignalSessionDestroy(session);
	}

	void CFSessionClient::OnSessionState(BaseSession* base_session, BaseSession::State state)
	{
		// StrSessionClient can only be used with a Session*, so it's
		// safe to cast here.
		Session* session = static_cast<Session*>(base_session);

		if(state == Session::STATE_RECEIVEDINITIATE)
			SignalSessionIncoming(session);
	}

	SessionDescription* CFSessionClient::CreateOffer(const std::string& session_type, const std::string& session_name)
	{
		CFContentDescription* session_content = new CFRtpContentDescription();
		CFContentDescription* channel_content = new CFChannelContentDescription();
		
		SessionDescription* offer = new SessionDescription();
		offer->AddContent("audio", session_type,   session_content);
		//offer->AddContent(session_type, session_type,   session_content);
		//offer->AddContent(session_name, "Channel Name", channel_content);
		return offer;
	}

	SessionDescription* CFSessionClient::CreateAnswer(const SessionDescription* offer) 
	{
		// The answer contains the intersection of the codecs in the offer with the
		// codecs we support, ordered by our local preference. As indicated by
		// XEP-0167, we retain the same payload ids from the offer in the answer.
		SessionDescription* accept = new SessionDescription();
		CFType type;  const ContentInfo* content = GetFirstSessionContent(offer, type);
		if(content){
			CFContentDescription* session_accept = NULL;
			switch(type)
			{
			case CFTYPE_SESSION_RTP:
				session_accept = new CFRtpContentDescription();
				break;
			}
			if(session_accept) accept->AddContent(content->name, content->type, session_accept);
		}

		return accept;
	}

	std::string CFSessionClient::GetSessionChannelName(const SessionDescription* offer)
	{
		CFType type;  const ContentInfo* content = GetFirstSessionContent(offer, type);
		if(content){
			switch(type)
			{
			default:
				content = GetFirstChannelContent(offer);
				if(content) return content->name;
			}
		}
		return "";
	}

	bool CFSessionClient::ParseContent(SignalingProtocol protocol, const buzz::XmlElement* content_elem, const ContentDescription** content, ParseError* error)
	{
		/*if (protocol == PROTOCOL_GINGLE) {
			const std::string& content_type = content_elem->Name().Namespace();
			if (NS_GINGLE_AUDIO == content_type) {
				return ParseGingleAudioContent(content_elem, content, error);
			} else if (NS_GINGLE_VIDEO == content_type) {
				return ParseGingleVideoContent(content_elem, content, error);
			} else {
				return BadParse("Unknown content type: " + content_type, error);
			}
		} else {
			std::string media;
			if (!RequireXmlAttr(content_elem, QN_JINGLE_CONTENT_MEDIA, &media, error))
				return false;

			if (media == JINGLE_CONTENT_MEDIA_AUDIO) {
				return ParseJingleAudioContent(content_elem, content, error);
			} else if (media == JINGLE_CONTENT_MEDIA_VIDEO) {
				return ParseJingleVideoContent(content_elem, content, error);
			} else {
				return BadParse("Unknown media: " + media, error);
			}
		}*/
		return true;
	}

	bool CFSessionClient::WriteContent(SignalingProtocol protocol, const ContentDescription* content, buzz::XmlElement** elem, WriteError* error)
	{
		/*const CFContentDescription* desc = static_cast<const CFContentDescription*>(content);
		bool crypto_required = secure() == SEC_REQUIRED;

		if (desc->type() == CFTYPE_SESSION_RTP) {
			const CFRtpContentDescription* rtp = static_cast<const CFRtpContentDescription*>(desc);

			if (protocol == PROTOCOL_GINGLE) {
				*elem = CreateGingleAudioContentElem(rtp, crypto_required);
			} else {
				*elem = CreateJingleAudioContentElem(rtp, crypto_required);
			}
		} else if (media->type() == CFTYPE_CHANNEL_NAME) {
			const CFChannelContentDescription* video = static_cast<const CFChannelContentDescription*>(desc);

			if (protocol == PROTOCOL_GINGLE) {
				*elem = CreateGingleVideoContentElem(video, crypto_required);
			} else {
				*elem = CreateJingleVideoContentElem(video, crypto_required);
			}
		} else {
			return BadWrite("Unknown content type: " + desc->type(), error);
		}*/

		return true;
	}

	const ContentInfo *GetFirstContent(const SessionDescription* sdesc, CFType type) 
	{
		if(sdesc == NULL) return NULL;

		const ContentInfos& contents = sdesc->contents();
		for(ContentInfos::const_iterator content = contents.begin(); content != contents.end(); content++){
			if(content->type == "urn:xmpp:jingle:apps:rtp:1"       ||
			   content->type == "Channel Name") {
				const CFContentDescription* cdesc = static_cast<const CFContentDescription*>(content->description);
				if(cdesc->type() == type)
					return &*content;
			}
		}
		return NULL;
	}

	const ContentInfo* GetFirstSessionContent(const SessionDescription* sdesc, CFType& type) 
	{
		const ContentInfo* info = NULL;
		type = CFTYPE_SESSION_RTP;
		if(info = GetFirstContent(sdesc, type)) return info;
		return info;
	}

	const ContentInfo* GetFirstChannelContent(const SessionDescription* sdesc) 
	{
		return GetFirstContent(sdesc, CFTYPE_CHANNEL_NAME);
	}
	
}  // namespace cricket
